﻿using Serilog;
using Serilog.Events;
using Serilog.Filters;
using SqlSugar;
using System.Text;

namespace PMIS.WebApi.Extensions
{
    public static class LoggerOutExtensions
    {
        /// <summary>
        /// 控制台
        /// </summary>
        /// <param name="loggerConfiguration"></param>
        /// <returns></returns>
        public static LoggerConfiguration WriteToConsole(this LoggerConfiguration loggerConfiguration)
        {
            //系统日志
            loggerConfiguration = loggerConfiguration.WriteTo.Logger(lg =>
                lg.RecordLog().WriteTo.Console());

            //sql日志
            loggerConfiguration = loggerConfiguration.WriteTo.Logger(lg =>
                lg.RecordSql(LoggerProperty.ToConsole).WriteTo.Console());

            return loggerConfiguration;
        }

        /// <summary>
        /// 文件
        /// </summary>
        /// <param name="loggerConfiguration"></param>
        /// <param name="logEvent"></param>
        /// <returns></returns>
        public static LoggerConfiguration WriteToFile(this LoggerConfiguration loggerConfiguration, HostBuilderContext context, LogEventLevel logEvent)
        {
            var rootPath = context.HostingEnvironment.ContentRootPath;
            //系统日志
            loggerConfiguration = loggerConfiguration.WriteTo.Logger(lg =>
                lg.Filter.ByIncludingOnly(p => p.Level == logEvent).RecordLog().WriteTo.Async(s =>
                    s.File(Path.Combine(rootPath, "Logs", logEvent.ToString(), ".log"),
                        rollingInterval: RollingInterval.Day,
                        outputTemplate: LoggerProperty.MessageTemplate, retainedFileCountLimit: 31,
                        encoding: Encoding.UTF8)));

            //sql日志
            loggerConfiguration = loggerConfiguration.WriteTo.Logger(lg =>
                lg.Filter.ByIncludingOnly(p => p.Level == logEvent).RecordSql(LoggerProperty.ToFile)
                    .WriteTo.Async(s =>
                        s.File(Path.Combine(rootPath, "Logs", "AopSql", ".log"),
                            rollingInterval: RollingInterval.Day,
                            outputTemplate: LoggerProperty.MessageTemplate, retainedFileCountLimit: 31,
                            encoding: Encoding.UTF8)));
            return loggerConfiguration;
        }

        /// <summary>
        /// Elasticsearch
        /// </summary>
        /// <param name="loggerConfiguration"></param>
        /// <returns></returns>
        //public static LoggerConfiguration WriteToElasticsearch(this LoggerConfiguration loggerConfiguration)
        //{
        //    //系统日志
        //    loggerConfiguration = loggerConfiguration.WriteTo.Logger(lg =>
        //        lg.RecordLog().WriteTo.Elasticsearch(
        //            new ElasticsearchSinkOptions(new Uri("http://127.0.0.1:9200/"))
        //            {
        //                AutoRegisterTemplate = true
        //            }));

        //    //sql日志
        //    loggerConfiguration = loggerConfiguration.WriteTo.Logger(lg =>
        //        lg.RecordSql(LoggerProperty.ToElasticsearch)
        //            .WriteTo.Elasticsearch(new ElasticsearchSinkOptions(new Uri("http://127.0.0.1:9200/"))
        //            {
        //                AutoRegisterTemplate = true
        //            }));

        //    return loggerConfiguration;
        //}


        public static LoggerConfiguration RecordSql(this LoggerConfiguration lc, string propertyName)
        {
            lc = lc.Filter.ByIncludingOnly(Matching.WithProperty<bool>(LoggerProperty.RecordSqlLog,
                s => s)).Filter.ByIncludingOnly(Matching.WithProperty<bool>(
                propertyName, s => s));
            return lc;
        }

        public static List<LogEvent> RecordSql(this List<LogEvent> batch)
        {
            //只记录 Insert、Update、Delete语句
            return batch.Where(s =>
                    s.WithProperty<bool>(LoggerProperty.RecordSqlLog, q => q) &&
                    s.WithProperty<bool>(LoggerProperty.ToDb, q => q))
                .Where(s => s.WithProperty<SugarActionType>(LoggerProperty.SugarActionType,
                    q => !new[] { SugarActionType.UnKnown, SugarActionType.Query }.Contains(q))).ToList();
        }

        public static LoggerConfiguration RecordLog(this LoggerConfiguration lc)
        {
            lc = lc.Filter.ByIncludingOnly(WithProperty<bool>(LoggerProperty.RecordSqlLog,
                s => !s));
            return lc;
        }

        public static List<LogEvent> RecordLog(this IEnumerable<LogEvent> batch)
        {
            return batch.Where(s => WithProperty<bool>(LoggerProperty.RecordSqlLog,
                q => !q)(s)).ToList();
        }

        public static Func<LogEvent, bool> WithProperty<T>(string propertyName, Func<T, bool> predicate)
        {
            return e =>
            {
                if (!e.Properties.TryGetValue(propertyName, out var propertyValue)) return true;

                return propertyValue is ScalarValue { Value: T value } && predicate(value);
            };
        }

        public static bool WithProperty<T>(this LogEvent e, string key, Func<T, bool> predicate)
        {
            if (!e.Properties.TryGetValue(key, out var propertyValue)) return false;

            return propertyValue is ScalarValue { Value: T value } && predicate(value);
        }
    }

    public class LoggerProperty
    {
        public static readonly string SugarActionType = "SugarActionType";
        public static readonly string RecordSqlLog = "RecordSqlLog";
        public static readonly string ToConsole = "ToConsole";
        public static readonly string ToFile = "OutToFile";
        public static readonly string ToDb = "ToDb";
        public static readonly string ToElasticsearch = "ToElasticsearch";

        public static readonly string MessageTemplate =
            "{NewLine}时间:{Timestamp:yyyy-MM-dd HH:mm:ss.fff}{NewLine}所在类:{SourceContext}{NewLine}等级:{Level}{NewLine}信息:{Message}{NewLine}{Exception}";
    }


}
